#include "writter.hpp"

#define ERROR(x) (throw (x))

namespace yxml
{
    void Writter::write(Document & document)
    {
        writeHeaderTag( document);
        writeNode(document);
    }

    void Writter::writeHeaderTag(Document & document)
    {
        _out << "<?xml ";
        writeAttribute("version", "1.0");
        _out << " ";
        writeAttribute("standalone", "yes");
        _out << " ";
        writeAttribute("encoding", document.encoding());
        _out << " ?>";
    }

    void Writter::writeNode(Node & node)
    {
        switch(node.type())
        {
        case TAG:
            writeTag(node);
            break;
        case TEXT:
            writeText(node);
            break;
        case COMMENT:
            writeComment(node);
            break;
        default:
            ERROR("Unknown Node Type");
            break;
        }
    }

    void Writter::writeTag(Node & node)
    {
        if(node.attributesCount())
        {
            _out << "<" << node.value();
            for(map<string,string>::const_iterator it = node.attributeBegin(); it != node.attributeEnd(); ++it)
            {
                _out << " ";
                writeAttribute(it->first, it->second);
            }
        }
        else
        {
            _out << "<"<<node.value();
        }
        if(node.count())
        {
            _out << ">";
            writeSubNodes(node);
            _out << "</" << node.value() << ">";
        }
        else
            _out << " />";
    }

    void Writter::writeSubNodes(Node& node)
    {
        Node *subNode = &node.firstChild();
        while(subNode->type() != NONE)
        {
            writeNode(*subNode);
            subNode = &subNode->nextBrother();
        }
    }


    void Writter::writeText(Node & node)
    {
        _out << fixText(node.value());
    }

    void Writter::writeComment(Node & node)
    {
        _out << "<!--" << fixText(node.value()) << "-->";
    }

    void Writter::writeAttribute(const string & key, const string & value)
    {
        _out << key << " = \"" << fixString(value) << "\"";
    }

    string Writter::fixString(string s)
    {
        //Replace all \s to \\s
        size_t pos = 0;
        while((pos=s.find('\\', pos) ) != string::npos)
        {
            s.insert(pos, "\\");
            pos+= 2;
        }
        pos = 0;
        //Replace all "s to \"s
        while((pos=s.find('\"', pos) ) != string::npos)
        {
            s.insert(pos, "\\");
            pos+= 2;
        }
        pos = 0;
        //Replace all 's to \'s
        while((pos=s.find('\'', pos) ) != string::npos)
        {
            s.insert(pos, "\\");
            pos+= 2;
        }
        return s;
    }

    string Writter::fixText(string s)
    {
        //Replace all & to &amp;
        size_t pos = 0;
        while((pos=s.find('&', pos) ) != string::npos)
        {
            s.insert(pos, "&amp;");
            pos+= 5;
        }
        pos = 0;
        //Replace all < to &lt;
        while((pos=s.find('<', pos) ) != string::npos)
        {
            s.insert(pos, "&lt;");
            pos+= 4;
        }
        pos = 0;
        //Replace all > to &gl;
        while((pos=s.find('>', pos) ) != string::npos)
        {
            s.insert(pos, "&gt;");
            pos+= 4;
        }
        return s;
    }


}
